<?php namespace App\Services;

use App\Exceptions\WxTransferException;
use Illuminate\Support\Facades\Log;

/**
 * 微信企业支付
 * Class WxMerchantTransferService
 * @package App\Services
 */

class WxMerchantTransferService
{
    protected $payurl = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
    protected $queryurl = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo';

    /**
     * 向用户转账
     * @param $amount integer 金额， 单位为分
     * @param $openId string  用户微信openId
     * @param $tradeNo integer   订单号
     * @param $clientIp string
     * @return mixed
     */
    public function transfer($amount, $openId, $tradeNo, $clientIp)
    {
        //封装成数据
        $dataArr = array();
        $dataArr['amount'] = $amount; //金额
        $dataArr['desc'] = "拼读分享奖励金"; //描述
        $dataArr['mchid'] = config('pay.wx-pay.MCHID');
        $dataArr['mch_appid'] = config('pay.wx-pay.APPID');
        $dataArr['check_name'] = "NO_CHECK"; //不检查用户名字
        $dataArr['openid'] = $openId; //用户唯一标识
        $dataArr['nonce_str'] = self::getNonceStr(); //nonce
        $dataArr['partner_trade_no'] = $tradeNo; //商户订单号
        $dataArr['spbill_create_ip'] = $clientIp;
        $dataArr['sign'] = $this->MakeSign($dataArr);
        return $this->pay($dataArr);
    }

    public function getTransferResult($tradeNo)
    {
        //封装成数据
        $dataArr = array();
        $dataArr['partner_trade_no'] = $tradeNo; //商户订单号
        $dataArr['mch_id'] = config('pay.wx-pay.MCHID');
        $dataArr['appid'] = config('pay.wx-pay.APPID');
        $dataArr['nonce_str'] = self::getNonceStr(); //nonce
        $dataArr['sign'] = $this->MakeSign($dataArr);

        $dataXml = "<xml>
        <appid>" . $dataArr['appid'] . "</appid>
        <mch_id>" . $dataArr['mch_id'] . "</mch_id>
        <nonce_str>" . $dataArr['nonce_str']. "</nonce_str>
        <partner_trade_no>" . $dataArr['partner_trade_no'] . "</partner_trade_no>
        <sign>" . $dataArr['sign'] . "</sign>
        </xml>";

        return $this->getSslCurl($this->queryurl, $dataXml);
    }

    /**
     * 支付方法， 不验证用户姓名
     * @param $data
     * @return mixed
     */
    protected function pay($data)
    {
        $dataXml = "<xml>
        <mch_appid>" . $data['mch_appid'] . "</mch_appid>
        <mchid>" . $data['mchid'] . "</mchid>
        <nonce_str>" .$data['nonce_str']. "</nonce_str>
        <partner_trade_no>" . $data['partner_trade_no'] . "</partner_trade_no>
        <openid>" . $data['openid'] . "</openid>
        <check_name>" . $data['check_name'] . "</check_name>
        <amount>" . $data['amount'] . "</amount>
        <desc>" . $data['desc'] . "</desc>
        <spbill_create_ip>" . $data['spbill_create_ip'] . "</spbill_create_ip>
        <sign>" . $data['sign'] . "</sign>
        </xml>";
        return $this->getSslCurl($this->payurl, $dataXml);
    }

    protected function getSslCurl($url, $data)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        $zs1 = __DIR__ . "/../../config/apiclient_cert.pem";
        $zs2 = __DIR__ . "/../../config/apiclient_key.pem";
        curl_setopt($ch, CURLOPT_SSLCERT, $zs1);
        curl_setopt($ch, CURLOPT_SSLKEY, $zs2);

        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $info = curl_exec($ch);

        if (curl_errno($ch)) {
            Log::error('Error ' . curl_error($ch));
        }

        curl_close($ch);
        $resultArray = $this->FromXml($info);
        return $resultArray;
    }

    /**
     *
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return string 产生的随机字符串
     */
    protected static function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function MakeSign($data)
    {
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->ToUrlParams($data);
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=".config('pay.wx-pay.KEY');
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }


    /**
     * 格式化参数格式化成url参数
     * @param $data
     * @return string
     */
    public function ToUrlParams($data)
    {
        $buff = "";
        foreach ($data as $k => $v)
        {
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 将xml转为array
     * @param string $xml
     * @return mixed
     */
    private function FromXml($xml)
    {
        if(!$xml){
            throw new WxTransferException();
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $values;
    }
}
